
/****************************************************************************
**
** Copyright (C) 2004-2008 VATATA.com . All rights reserved.
**
** This file is the part of the Miniutil Poject of VATATA.com.
** Vatata's miniutil library is lightweight and portable (for linux
** and Windows). The C++ Class Library included Threadpool, Dll danymic loading, 
** General socket application model and tcp server, httpserver/servlet, etc. 
** It is a pratical utility disigned for who don't want learn the Big C++ 
** Developing Platform, such as  Boost or ACE.
**
** This file may be used under the terms of the GNU General Public
** License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file.  Alternatively you may (at
** your option) use any later version of the GNU General Public
** License if such license has been publicly approved by VATATA.com.
** (or its successors, if any) and the Miniutil Project Aministrators. 
**
** Please review the following information to ensure GNU General
** Public Licensing requirements will be met:
** http://www.gnu.org/licenses/lgpl.html. If you are unsure which 
** license is appropriate for your use, please review the following 
** information: http://code.google.com/p/miniutil/ or contact 
** http://www.vatata.com.
**
** In addition, as a special exception, VATATA.com, as the sole
** copyright holder for Miniutil Project, grants users of VATATA
** P2P Platform the right to release and package with the related 
** libraries without the source code.
**
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE. vatata reserves all rights not expressly
** granted herein.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/



#include "miniutil/pattern.h"

#include "pcre.h"

#include <string.h>
#include <stdlib.h>


using namespace std;
using namespace miniutil;
	

int pattern::CASE_INSENSITIVE = PCRE_CASELESS ;
int pattern::MULTILINE = PCRE_MULTILINE ;
int pattern::DOTALL  = PCRE_DOTALL ;

pattern::pattern( const char * patternStr, int flag )  :
	_patternStr( patternStr ),
	_flag(flag)
{
	const char * error;
	int errorOffset;
	_re = pcre_compile( patternStr, _flag, &error, &errorOffset, NULL );
	if ( _re != NULL )
	{
		int rc = pcre_fullinfo( (const pcre *)_re, NULL , PCRE_INFO_CAPTURECOUNT, &_matchCount);
		//printf( "matchCount %d \n", _matchCount );
		if ( rc )
		{
//			pcre_free( _re );
			free( _re );
			_re = NULL;
		}
	}
}

pattern::~pattern()
{
	if ( _re != NULL )
		free( _re );
//			pcre_free( _re );
}

int pattern::_parseFlag( const char * flagStr )
{
	int flag = 0;
	if ( strchr(flagStr, 'm' ) >= 0 )
		flag |= MULTILINE;
	if ( strchr(flagStr, 'i' ) >= 0 )
		flag |= CASE_INSENSITIVE;
	return flag;
}


pattern *pattern::compile( const char *  patternStr, int flag )
{
	pattern *_p = new pattern( patternStr, flag );
	if ( _p == NULL )
		return NULL;
	else if ( _p->_re == NULL )
	{
		delete _p;
		return NULL;
	}
	return _p;
}


matcher * pattern::getMatcher(const char * input )
{
	return new matcher(this, input);
}

int pattern::match( const char * input )
{
	matcher mch( this, input );

	return mch.find();
}

int pattern::match( const char * input, vector<string> &list )
{
	matcher mch( this, input );
	return  mch.match(list);
}

int pattern::matchAll( const char * input, vector<string>& list )
{
	matcher mch(this, input);
	return mch.matchAll( list );
}

int pattern::replace( const char *input, const char * replacement, string & result )
{
	matcher mch(this, input );
	return mch.replace( result, replacement );
}

int pattern::replaceAll( const char * input , const char * replacement, string & result)
{
	matcher mch(this, input );
	return mch.replaceAll( result, replacement );
}

int pattern::split(const char *input, std::vector<string> &list, int limit )
{
	matcher mch(this, input );
	return mch.split( list, limit );
}


int pattern::match( const char * input, const char * patternStr , int flag )
{
	pattern * _p = pattern::compile( patternStr, flag );
	if ( _p == NULL )
		return -1;
	matcher * _m = _p->getMatcher( input );

	int r = _m->find();
	delete _m;
	delete _p;
	return r;
}

bool pattern::replace( const char * input, const char * patternStr, const char* replacement, string & result, int flag )
{
	pattern * _p = pattern::compile( patternStr, flag );
	if ( _p == NULL )
		return false;
	matcher * _m = _p->getMatcher( input );

	bool r = ( _m->replace(  result, replacement ) > 0 );
	delete _m;
	delete _p;
	return r;
}

int pattern::replaceAll( const char * input, const char * patternStr, const char* replacement, string & result, int flag )
{
	pattern * _p = pattern::compile( patternStr, flag );
	if ( _p == NULL )
		return -1;
	matcher * _m = _p->getMatcher( input );
	int r = _m->replaceAll(  result, replacement );
	delete _m;
	delete _p;
	return r;
}

int pattern::split( const char * input , const char * patternStr, vector<string> &list , int flag, int limit )
{
	pattern * _p = pattern::compile( patternStr, flag );
	if ( _p == NULL )
		return -1;
	matcher * _m = _p->getMatcher( input );
	int r = _m->split(  list, limit );
	delete _m;
	delete _p;
	return r;
}

int pattern::match( const char * input, const char * patternStr, vector<string> &list ,int flag )
{
	pattern * _p = pattern::compile( patternStr, flag );
	if ( _p == NULL )
		return -1;
	matcher * _m = _p->getMatcher( input );
    int r = _m->match(  list );
	delete _m;
	delete _p;
	return r;
}


matcher::matcher( pattern *_p, const char * input ):
	inputStr(input),
	hasExecuted(false)
{
	if ( _p == NULL )
		return;
	p_pattern = _p;
	this->resultSize = ( p_pattern->_matchCount + 1 ) * 3;
	this->matchResult = (int * ) malloc(sizeof(int) * resultSize );
//	printf( "resultSize %d  %p \n", resultSize, result );
}

matcher::~matcher()
{
	if ( this->matchResult != NULL )
	{
		free ( matchResult );
		matchResult = NULL;
	}
}




pattern * matcher::getPattern()
{
	return p_pattern;
}


int matcher::find(int startIndex )
{
	if ( this->hasExecuted )
	{
		startIndex = this->matchResult[1];
		if ( startIndex == this->prevStartIndex )
			startIndex ++;
	}

//	printf("\n\t\tfind at index %d %d, %s ", startIndex, matchResult[1], inputStr.substr(startIndex).c_str() );

	this->substrCount = pcre_exec( (const pcre *) p_pattern->_re, NULL, inputStr.c_str() ,(int) inputStr.length() , startIndex, 0, matchResult, resultSize );
	this->hasExecuted = true;
	this->prevStartIndex = startIndex;

//	printf("result %d %d %d \n", matchResult[0], matchResult[1] , prevStartIndex);

	if ( this->substrCount < 0 )
		return -1;
	return matchResult[0];

}
//
//int matcher::matchedCount()
//{
//	return p_pattern->_matchCount;
//}

bool matcher::getMatchedStr(string & mstr, int index )
{
	if ( this->hasExecuted == false )
		return false;
	if ( this->substrCount < 0 )
		return false;
	if ( index < 0 )
		return false;
	if ( index >= this->substrCount )
		return false;

	if ( this->matchResult[ index * 2 ] < 0 )
		mstr = "";
	else
		mstr = this->inputStr.substr(matchResult[index*2], matchResult[index*2+1]-matchResult[index*2]);
	return true;
}

int matcher::match(vector<string> & mlist )
{
	mlist.clear();
	if ( this->hasExecuted == false )
		if ( this->find() < 0 )
			return -1;
	int i = 0;
	for( i=0; i< this->substrCount; i++ )
	{
		if ( matchResult[i*2] < 0 )
			break;
		mlist.push_back( inputStr.substr( matchResult[i*2], matchResult[i*2+1]-matchResult[i*2]) );
	}
	return i;
}

bool matcher::match()
{
	int rc = pcre_exec( (const pcre *) p_pattern->_re, NULL , inputStr.c_str(),(int) inputStr.length(), 0, 0 , NULL , 0 );
	return ( rc >= 0 );
}

int matcher::getMatchedStrStart( int index )
{
	if ( this->hasExecuted == false )
		return -1;
	if ( this->substrCount < 0 )
		return -1;
	if ( index < 0 )
		return -1;
	if ( index >= this->substrCount )
		return -1;

	return matchResult[index*2];
}

int matcher::getMatchedStrEnd( int index )
{
	if ( this->hasExecuted == false )
		return -1;
	if ( this->substrCount < 0 )
		return -1;
	if ( index < 0 )
		return -1;
	if ( index >= this->substrCount )
		return -1;

	return matchResult[index*2+1];
}


void matcher::reset()
{
	this->hasExecuted = false;
	this->prevStartIndex = 0;
}

void matcher::reset( const char * input )
{
	this->inputStr = input;
	reset();
}


int matcher::replace( string &result, const char * replacement )
{
	if ( this->hasExecuted == false )
		if ( this->find() == -1 )
			return -1;
	if ( this->substrCount < 1 )
		return -1;

//	printf("replace %d %p\n", resultSize, result );

	int startPos = matchResult[0] ;
	int endPos =  matchResult[1];

	result = inputStr.substr(0, startPos );

	int len =(int) strlen(replacement);
	for( int i = 0; i< len; i++ )
	{
		char ch = replacement[i];
		if ( ch == '$' && i < len - 1 )
		{
			ch = replacement[++i];
			if ( ch >= '0' && ch <= '9' )
			{
				int index = ch - '0';
				if ( index >= this->substrCount )
					continue;
				result.append( inputStr.substr(matchResult[index*2], matchResult[index*2+1] - matchResult[index*2] ) );
				continue;
			}
		}
		result.append( &ch, 1);
	}
	result.append( inputStr.substr(endPos ) );
	return startPos;
}

int matcher::replaceAll(string &result, const char * replacement )
{
	this->reset();
	int count = 0;
	int appendPos = 0;

	result = "";
	while( find( ) >= 0 )
	{
		if ( this->substrCount < 1 )
			break;
		int startPos = matchResult[0] ;
		result.append( inputStr.substr(appendPos, startPos - appendPos ) );
//		printf("\n\t startPos %d  %s %s\n", startPos, inputStr.substr(startPos).c_str(), result.c_str() );
		appendPos = matchResult[1] ;

		int len =(int) strlen(replacement);
		for( int i = 0; i< len; i++ )
		{
			char ch = replacement[i];
			if ( ch == '$' && i < len - 1 )
			{
				ch = replacement[++i];
				int index = ch - '0';
//				printf("\t\t find $ %c  %d %d \n", ch , index, substrCount);
				if ( index >= 0 && index <= 9 )
				{
					if ( index >= this->substrCount )
						continue;
					result.append( inputStr.substr(matchResult[index*2], matchResult[index*2+1] - matchResult[index*2] ) );
//					printf("\t\t find $  %s\n", inputStr.substr(matchResult[index*2], matchResult[index*2+1] - matchResult[index*2] ).c_str() );
					continue;
				}
			}
			result.append(&ch,1);
		}

		count ++;
	}
	if ( appendPos < (int)inputStr.length() )
		result.append( inputStr.substr(appendPos) );

	return count;
}

int matcher::split( vector<string> & list, int limit )
{
	list.clear();
	if ( limit <= 0 )
		return 0;
	else if ( limit == 1 )
	{
		list.push_back( inputStr );
		return 1;
	}

	this->reset();
	int count = 1;
	int startPos;
	int endPos = 0;
	while( count < limit && find() >= 0 )
	{
		if ( this->substrCount < 1 )
			break;
		startPos = matchResult[0];
		if ( startPos > endPos )
		{
			count++;
			list.push_back( inputStr.substr( endPos , startPos - endPos ) );
		}
		endPos = matchResult[1] ;
	}
	if ( endPos < (int)inputStr.length() )
		list.push_back( inputStr.substr( endPos ));

	return count;
}

int matcher::matchAll( vector<string> & list )
{
	list.clear();
	this->reset();
	int count = 0;
	while( find() >= 0 )
	{
		if ( this->substrCount < 1 )
			break;
		count++;
		int startPos = matchResult[0];
		int endPos = matchResult[1] ;
		list.push_back( inputStr.substr( startPos , endPos - startPos ) );
	}
	return count;
}


